/*
 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Autogenerated file -- DO NOT EDIT!

#ifndef PANDA_<%= Common::module.name.upcase %>_OPTIONS_GEN_H
#define PANDA_<%= Common::module.name.upcase %>_OPTIONS_GEN_H

#include "utils/pandargs.h"
#include "platforms/target_defaults/default_target_options.h"
#include "macros.h"

#include <optional>
#include <string>
#include <unordered_set>
#include <vector>

namespace <%= Common::module.namespace %> {
% Common::options.select { |op| op.has_enum? }.each do |op|
namespace <%= op.camel_name.snakecase %> {
enum Enum : size_t {
%   op.flat_enum.each do |enumerator|
    <%= enumerator.snakecase.upcase %>,
%   end
%   op.sub_enums.each do |name, sub_enum|
    <%= name.snakecase.upcase %>_FIRST = <%= sub_enum[0].snakecase.upcase %>,
    <%= name.snakecase.upcase %>_LAST = <%= sub_enum[-1].snakecase.upcase %>,
%   end
    FIRST = <%= op.flat_enum[0].snakecase.upcase %>,
    LAST = <%= op.flat_enum[-1].snakecase.upcase %>,
    COUNT = LAST - FIRST + 1,
    INVALID = COUNT,
};

inline auto FromString(std::string_view str) {
    static const std::map<std::string_view, Enum> MAP {
%   op.flat_enum.each do |enumerator|
        {"<%= enumerator %>", <%= enumerator.snakecase.upcase %>},
%   end
    };
    if (MAP.find(str) == MAP.end()) {
        return INVALID;
    }
    return MAP.at(str);
}

constexpr std::string_view ToString(Enum e) {
    constexpr std::array<std::string_view, COUNT> MAP {
%   op.flat_enum.each do |enumerator|
        "<%= enumerator %>",
%   end
    };
    return MAP[e];
}

}  // namespace <%= op.camel_name.snakecase %>

% end
// NOLINTBEGIN(readability-identifier-naming)
class PANDA_PUBLIC_API Options {
public:
    class Error {
    public:
        explicit Error(std::string msg) : msg_(std::move(msg)) {}

        std::string GetMessage() {
            return msg_;
        }
    private:
        std::string msg_;
    };

    explicit Options(const std::string &exe_path) :
        exe_dir_(GetExeDir(exe_path))
% Common::options.each do |op|
%   next if op.sub_option?
%   next if op.deprecated?
%   if op.default_target_specific?
        ,<%= op.field_name %>{"<%= op.name %>", <%= op.default_value %>, <%= op.full_description %>}
% end
% end
    {}

    // NOLINTBEGIN(readability-function-size)
    void AddOptions(PandArgParser *parser) {
        parser->Add(&version_);
% Common::options.each do |op|
%   next if op.sub_option?
%   next if op.deprecated?
        parser->Add(&<%= op.field_name %>);
% end
    }
    // NOLINTEND(readability-function-size)

    bool IsVersion() const {
        return version_.GetValue();
    }

    void SetVersion(bool value) {
        version_.SetValue(value);
    }

    bool WasSetVersion() {
        return version_.WasSet();
    }

% Common::options.each do |op|
%   if !op.lang_specific? && !op.has_lang_suboptions?
    <%= op.getter_signature %> {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
        return <%= op.field_name %>.GetValue();
    }

    void <%= op.setter_name %>(<%= op.type %> value) {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
        <%= op.field_name %>.SetValue(<%= op.type == 'std::string' || op.type == 'arg_list_t' ? 'std::move(value)' : 'value' %>);
    }

    bool WasSet<%= op.camel_name %>([[maybe_unused]] std::string_view lang = "") const {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
        return <%= op.field_name %>.WasSet();
    }
%   end
%   if op.has_lang_suboptions?
    <%= op.getter_signature %> {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
%     op.lang.each do |lang|
        if (lang == "<%= "#{lang}" %>") {
            return WasSet<%= op.lang_camel_name(lang) %>() ? <%= op.lang_field_name(lang) %>.GetValue() : <%= op.field_name %>.GetValue();
        }
%     end
        return <%= op.field_name %>.GetValue();
    }

    void <%= op.setter_name %>(<%= op.type %> value) {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
        <%= op.field_name %>.SetValue(<%= op.type == 'std::string' || op.type == 'arg_list_t' ? 'std::move(value)' : 'value' %>);
    }

    bool WasSet<%= op.camel_name %>(std::string_view lang) const {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
%     op.lang.each do |lang|
        if (lang == "<%= "#{lang}" %>") {
            return <%= op.lang_field_name(lang) %>.WasSet();
        }
%     end
        return <%= op.field_name %>.WasSet();
    }
%   end
% end
// NOLINTNEXTLINE(readability-function-size)
    std::optional<Error> Validate() const {
% Common::options.each do |op|
%   next unless defined? op.possible_values
%   is_string = op.type == 'std::string' || op.type == 'arg_list_t'
%   possible_values = op.possible_values.map { |e| is_string ? Common::to_raw(e) : e }.join(', ')
        {
            std::unordered_set<<%= is_string ? "std::string" : op.type %>> possible_values{<%= possible_values %>};
%   if op.type != 'arg_list_t'
            std::vector<<%= op.type %>> values{<%= op.field_name %>.GetValue()};
%   else
            const auto &values = <%= op.field_name %>.GetValue();
%   end
            for (const auto &value : values) {
                if (possible_values.find(value) == possible_values.cend()) {
                    return Error("argument --<%= op.name %>: invalid value: '" + <%= is_string ? "value" : "std::to_string(value)" %> + \
                                 R"('. Possible values: <%= op.possible_values %>)");
                }
            }
        }
% end
        return {};
    }

private:
% Common::options.each do |op|
%   if op.lang_specific?
    bool WasSet<%= op.camel_name %>() const {
%   if op.deprecated?
        std::cerr << "WARNING: Option '<%= op.name %>' is deprecated and should not be used\n";
%   end
        return <%= op.field_name %>.WasSet();
    }
%   end
% end

    static std::string GetExeDir(const std::string &exe_path) {
#ifdef PANDA_TARGET_WINDOWS
        const char preferred_separator = '\\';
#else
        const char preferred_separator = '/';
#endif
        auto pos = exe_path.find_last_of(preferred_separator);
        if (pos == std::string::npos) {
            return ".";
        }
        return exe_path.substr(0, pos);
    }

% Common::options.each do |op|
%   next unless op.need_default_constant
    static constexpr <%= op.type %> <%= op.default_constant_name %> = <%= op.default %>;

% end
    std::string exe_dir_;
    PandArg<bool> version_{"version", false, R"(Ark version, file format version and minimum supported file format version)"};
% Common::options.each do |op|
% if op.default_target_specific?
    PandArg<<%= op.type %>> <%= op.field_name %>;
% elsif defined? op.delimiter
    PandArg<<%= op.type %>> <%= op.field_name %>{"<%= op.name %>", <%= op.default_value %>, <%= op.full_description %>, "<%= op.delimiter %>"};
% elsif defined? op.range
%   min, max = op.range.match('(\d+)\D*(\d+)').captures;
%   if op.default.to_i > max.to_i || op.default.to_i < min.to_i
%     abort "FAILED: Default value of argument " + op.name + " has out of range default parameter"
%   end
    PandArg<<%= op.type %>> <%= op.field_name %>{"<%= op.name %>", <%= op.default_value %>, <%= op.full_description %>, <%= min.to_i %>, <%= max.to_i %>};
% elsif op.has_sub_options?
    PandArgCompound <%= op.field_name %>{"<%= op.name %>", <%= op.full_description %>, {<%= op.sub_options.map{|o| '&' + o.field_name}.join(', ') %>}};
% else
    PandArg<<%= op.type %>> <%= op.field_name %>{"<%= op.name %>", <%= op.default_value %>, <%= op.full_description %>};
% end
% end
};
// NOLINTEND(readability-identifier-naming)
}  // namespace <%= Common::module.namespace %>

#endif  // PANDA_<%= Common::module.name.upcase %>_OPTIONS_GEN_H
